/*
 * A class to implement KMeans Algorithm
 * Author : TuanNA
 * */
package kmeansclustering;

import java.io.*;
import java.nio.file.Files;
import java.util.*;

public class KMeans {

    private int sumOfCluster;
    private String filePath;
    private String[] labels;
    public ArrayList<Float> sumErrors;
    public float[][] data;
    public float[] data1D;
    public float[][] data2D;
    public float[][][] data3D;
    private int typeOfDistance;
    public int sumOfLine;
    public int sumOfColumn;
    public float threshold = 100.0f;
    public ArrayList<Cluster> clusters = new ArrayList<>();

    /*
     * Chú ý : load data ngay khi khởi tạo đối tượng khong can load them data
     * nua
     *
     */
    public KMeans(int sumOfCluster, String filePath) throws FileNotFoundException {
        this.sumErrors = new ArrayList<>();
        this.sumOfCluster = sumOfCluster;
        this.filePath = filePath;

        // Tam thoi fix cung 100x24
        this.sumOfLine = 100; //countLine(this.filePath);
        this.sumOfColumn = 24;
        loadData();
        // add default (not random)
        for (int i = 0; i < sumOfCluster; i++) {
            this.clusters.add(new Cluster());
        }
    }

    // count line in a file
    public int countLine(String filePath) {
        int sumOfLine = 0;
        try {
            FileReader inputFile = new FileReader(this.filePath);
            BufferedReader bufferReader = new BufferedReader(inputFile);
            String line;
            while ((line = bufferReader.readLine()) != null) {
                sumOfLine += 1;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return sumOfLine;
    }

    // load data from txt file
    public void loadData() throws FileNotFoundException {
        try (Scanner input = new Scanner(new File(this.filePath))) {
            this.data = new float[sumOfLine][sumOfColumn];
            this.labels = new String[sumOfLine];

            for (int i = 0; i < sumOfLine; i++) {
                for (int j = 0; j < sumOfColumn; j++) {
                    if (input.hasNextFloat()) {
                        this.data[i][j] = (float) input.nextFloat();
                    }
                }
                this.labels[i] = input.nextLine();
            }
        }
    }

    //////////////////////////
    public void writeOutputFile(String fileName) throws FileNotFoundException {
        /*
         * FileOutputStream fos = new FileOutputStream(fileName, true);
         *
         * PrintWriter pw = pw = new PrintWriter(fos);
         *
         * String diem; String ten; String rowContent; String label; for (int i
         * = 0; i < sumOfCluster; i++) { Cluster tempCluster =
         * this.clusters.get(i); for (int j = 0; j <
         * tempCluster.listSamples.size(); j++) { label = this.labels[i];
         *
         * int t = label.length(); diem = label.substring(t - 4, t - 1); ten =
         * label.substring(0, t - 6);
         *
         * rowContent = i + "\t" + diem + "\t" + ten; pw.println(rowContent); }
         * } pw.close();
         *
         */
        String s = "";
        byte data[] = s.getBytes();

        try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(CREATE, APPEND))) {

            out.write(data, 0, data.length);
        } catch (IOException x) {
            System.err.println(x);
        }
    }

    // random k centroid from dataset, id of centroid = id of cluster
    public ArrayList<Integer> randCentroid() {
//		int sumOfLine = countLine(this.filePath);
        ArrayList<Integer> centroids = new ArrayList<>();
        Random random = new Random();
        while (centroids.size() < this.sumOfCluster) {
            int centroid = random.nextInt(sumOfLine);
            if (!centroids.contains(centroid)) {
                centroids.add(centroid);
            }
        }

        return centroids;
    }

    // Init random as kMeans algorithm
    public void initCentroid() {
        // create ID array with 0 <= ID < 100
        int[] idArray = new int[this.sumOfCluster];
        int i = 0;
        do {
            int randID = (int) (Math.random() * 100);
            boolean checkExist = false;
            for (int j = 0; j <= i; j++) {
                if (randID == idArray[i]) {
                    checkExist = true;
                    break;
                }
            }
            if (!checkExist) {
                i++;
                idArray[i] = randID;
            }
        } while (i < this.sumOfCluster - 1);

        // add data[ID] to become centroid & set parrent
        for (int j = 0; j < this.sumOfCluster; j++) {
            this.clusters.get(j).setCentroid(this.data[idArray[j]]);
            this.clusters.get(j).setParrent(this);
        }
    }

    // Init random as kMeans algorithm
    public void initRandomCentroid() {
    }

    // Calculate sum of Error in each cluster=> tong binh phuong
    public float calculateSumError() {
        float error = 0;
        Iterator<Cluster> iterator = this.clusters.iterator();
        while (iterator.hasNext()) {
            error += iterator.next().sumError;
        }
        return error;
    }

    public void clustering() throws FileNotFoundException {
        initCentroid();
        int count = 0;
        while (true) {
            count++;
            for (int i = 0; i < 100; i++) {
                float minDistance = 128374861f;
                int indexMinDistance = -1;
                int j;
                for (j = 0; j < sumOfCluster; j++) {
                    float temp = clusters.get(j).getSquareDistance(i);
                    if (temp < minDistance) {
                        indexMinDistance = j;
                        minDistance = temp;
                    }
                }
                if (indexMinDistance < 0) {
                    System.out.println("Data mistake: BREAK");
                    System.exit(0);
                } else {
                    clusters.get(indexMinDistance).addSample(i, minDistance);
                }
            }
            for (int i = 0; i < sumOfCluster; i++) {
                Cluster tempCluster = clusters.get(i);
                tempCluster.resetCentroid();
            }
            float tempErr = calculateSumError();
            sumErrors.add(tempErr);
            int sizeOfSumErrs = sumErrors.size();
            if (sizeOfSumErrs >= 2) {
                if ((int) (tempErr - sumErrors.get(sizeOfSumErrs - 2)) == 0) {
                    break;
                }
            }
        }
    }
}